package com.loveprogrammer.eternity.action;

import com.loveprogrammer.eternity.enums.BattleTypeEnum;
import com.loveprogrammer.eternity.exception.InterruptException;
import com.loveprogrammer.eternity.factory.template.MapTemplate;
import com.loveprogrammer.eternity.model.Character;
import com.loveprogrammer.eternity.model.Monster;
import com.loveprogrammer.eternity.common.Tuple;
import com.loveprogrammer.eternity.model.game.HeroWrapper;
import com.loveprogrammer.eternity.model.product.Coin;
import com.loveprogrammer.eternity.model.product.FallingCoin;
import com.loveprogrammer.eternity.model.product.FallingGoods;
import com.loveprogrammer.eternity.model.product.Goods;
import com.loveprogrammer.eternity.notify.Notify;
import com.loveprogrammer.eternity.skill.Skill;
import com.loveprogrammer.eternity.skill.State;
import com.loveprogrammer.eternity.skill.support.states.DazeState;
import com.loveprogrammer.eternity.utils.BeanCopyUtils;
import io.netty.util.HashedWheelTimer;
import org.apache.poi.ss.formula.functions.T;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * @version 1.0.0
 * @description: 战斗主类
 * @author: eric
 * @date: 2022-07-19 18:42
 **/
public class Battle extends BaseAction {

    // 主战斗线程池
    private final ScheduledExecutorService executorService;

    // 附属战斗线程池，计算每秒中的各种状态
    private final ScheduledExecutorService subFightExecutorService;

    private List<MapTemplate.Drop> dropList;

    public Battle(ScheduledExecutorService executorService, ScheduledExecutorService subFightExecutorService) {
        this.executorService = executorService;
        this.subFightExecutorService = subFightExecutorService;
        notify = new Notify(this);
    }

    // 全局的监听器
    private Map<Character, ScheduledFuture> futureMap = new HashMap<>();

    private BattleTypeEnum battleType = BattleTypeEnum.PVE;

    private HeroWrapper attackHero;

    // 分为两个阵营，一个阵营A，一个阵营B
    private List<? extends Character> campA;

    private List<? extends Character> campB;

    private List<Goods> fallingGoods = new ArrayList<>();

    private Coin fallingCoin = new Coin();

    private FallingGoods fallingGoodsCalculator;

    private FallingCoin fallCoinCalculator;

    private int fallingExp = 0;

    private int max_monster_level = 0;

    // 设定两个锁
    private final ReentrantLock reentrantLockA = new ReentrantLock();
    private final ReentrantLock reentrantLockB = new ReentrantLock();

    public Boolean fight() {

        // 召唤术先行发动
        launchPassiveCallSkill(campA, campB);

        // 光环注册 首先所有的被动技能释放 例如增速 减甲 增力 增魔 增技巧 减技巧等等 说白了就是光环
        launchPassiveHaloSkill(campA, campB);

        // final StringBuilder builder = new StringBuilder();
        for (Character character : campA) {
            ScheduledFuture<?> futureA =
                    executorService.scheduleWithFixedDelay(
                            new Runnable() {
                                public void run() {
                                    // 回合制的必须要一个一个打
                                    while (reentrantLockA.isLocked()) {
                                        // System.out.println("等待解锁");
                                        try {
                                            Thread.sleep(100);
                                        } catch (InterruptedException e) {
                                            // e.printStackTrace();
                                        }
                                    }
                                    reentrantLockB.lock();
                                    try {
                                        if (campB != null && campB.size() > 0) {
                                            if (character.getCurrentHp() > 0) {
                                                // 先检查眩晕情况
                                                checkState(character, campA, campB, reentrantLockB);
                                                if (character.getCurrentHp() <= 0) {
                                                    return;
                                                }

                                                Tuple<String, List<Character>> attack = null;
                                                // 检查技能冷却情况
                                                List<Skill> skills = character.getSkills();
                                                if (!skills.isEmpty()) {
                                                    // TODO 需要单独一个线程池来消减技能的冷却时间 目前先已攻击次数作为冷却时间，后面要改成以时间作为冷却时间
                                                    skills.forEach(Skill::reduceInterval);
                                                    //
                                                    List<Skill> skillList = skills.stream().filter(e -> e.restInterval() == 0).collect(Collectors.toList());
                                                    if (!skillList.isEmpty()) {
                                                        attack = character.tryToLaunchSkill(skillList, campA,
                                                                campB, subFightExecutorService);
                                                    }
                                                }
                                                // 技能没有发动 普通攻击
                                                if (attack == null) {
                                                    attack = character.attack(campB);
                                                }
                                                eventManager.notifyMessage(attack.getKey(), 1);
                                                List<Character> defences = attack.getValue();
                                                for (Character defence : defences) {
                                                    if (defence.checkDead()) {
                                                        publishDeadEvent(defence, campB);
                                                    }
                                                }
                                            } else {
                                                publishDeadEvent(character, campA);
                                            }
                                        } else {
                                            // 如果campB为空就结束了
                                            publishDeadEvent(null, campB);
                                        }
                                    } catch (Exception exception) {
                                        if (exception instanceof InterruptException) {

                                        } else {
                                            exception.printStackTrace();
                                        }
                                    } finally {
                                        reentrantLockB.unlock();
                                    }
                                }
                            }, 500 + (int) (((1 - (character.getSpeed()) * 1.0 / (2000 + character.getSpeed()))
                                    * (1 - (character.getSpeed()) * 1.0 / (2000 + character.getSpeed()))) * 5000),
                            500 + (int) (((1 - (character.getSpeed()) * 1.0 / (2000 + character.getSpeed()))
                                    * (1 - (character.getSpeed()) * 1.0 / (2000 + character.getSpeed()))) * 5000), TimeUnit.MILLISECONDS);
            futureMap.put(character, futureA);
            //
        }

        for (Character character : campB) {
            int level = character.getLevel();
            max_monster_level = Math.max(level, max_monster_level);
            ScheduledFuture<?> futureB
                    = executorService.scheduleWithFixedDelay(
                    new Runnable() {
                        public void run() {
                            while (reentrantLockB.isLocked()) {
                                System.out.println("等待解锁");
                                try {
                                    Thread.sleep(10);
                                } catch (InterruptedException e) {
                                    // e.printStackTrace();
                                }
                            }
                            reentrantLockA.lock();
                            try {
                                if (campA != null && campA.size() > 0) {
                                    if (character.getCurrentHp() > 0) {
                                        // 先检查眩晕情况
                                        checkState(character, campA, campB, reentrantLockA);
                                        // 再次检查是否已经死亡
                                        if (character.getCurrentHp() <= 0) {
                                            return;
                                        }

                                        Tuple<String, List<Character>> attack = null;
                                        // 检查技能冷却情况
                                        List<Skill> skills = character.getSkills();
                                        if (!skills.isEmpty()) {
                                            // TODO 需要单独一个线程池来消减技能的冷却时间 目前先已攻击次数作为冷却时间，后面要改成以时间作为冷却时间
                                            skills.forEach(Skill::reduceInterval);
                                            //
                                            List<Skill> skillList = skills.stream().filter(e -> e.restInterval() == 0).collect(Collectors.toList());
                                            if (!skillList.isEmpty()) {
                                                attack = character.tryToLaunchSkill(skillList, campB, campA, subFightExecutorService);
                                            }
                                        }
                                        // 技能没有发动 普通攻击
                                        if (attack == null) {
                                            attack = character.attack(campA);
                                        }

                                        eventManager.notifyMessage(attack.getKey(), 2);
                                        List<Character> value = attack.getValue();
                                        for (Character defence : value) {
                                            if (defence.checkDead()) {
                                                publishDeadEvent(defence, campA);
                                            }
                                        }
                                    } else {
                                        publishDeadEvent(character, campB);
                                    }
                                } else {
                                    publishDeadEvent(null, campA);
                                }
                            } catch (Exception exception) {
                                if (exception instanceof InterruptException) {

                                } else {
                                    exception.printStackTrace();
                                }
                            } finally {
                                reentrantLockA.unlock();
                            }

                        }
                    }, 500 + (int) (((1 - (character.getSpeed()) * 1.0 / (2000 + character.getSpeed()))
                            * (1 - (character.getSpeed()) * 1.0 / (2000 + character.getSpeed()))) * 5000),
                    500 + (int) (((1 - (character.getSpeed()) * 1.0 / (2000 + character.getSpeed()))
                            * (1 - (character.getSpeed()) * 1.0 / (2000 + character.getSpeed()))) * 5000), TimeUnit.MILLISECONDS);
            futureMap.put(character, futureB);
        }
        while (!executorService.isShutdown()) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        if (subFightExecutorService != null) {
            subFightExecutorService.shutdownNow();
        }
        if (campB.size() == 0 && battleType == BattleTypeEnum.PVE) {
            eventManager.notifyMessage("获取掉落的物品", 1);
            attackHero.getWarehouse().pickUp(fallingGoods);
            attackHero.getHero().tryToWearAndSale(fallingGoods);
            attackHero.getWarehouse().pickUp(fallingCoin);
        }
        // timer.start();
        // timer = null;
        return campB.size() == 0;
    }

//    private void checkAndLaunchStates(List<? extends Character> campA, List<? extends Character> campB) {
//        // 把所有的状态都run一遍
//        for (Character character : campA) {
//            List<State> states = character.getStates();
//            for (State state : states) {
//                state.run();
//            }
//        }
//        for (Character character : campB) {
//            List<State> states = character.getStates();
//            for (State state : states) {
//                state.run();
//            }
//        }
//    }

    private void checkState(Character character, List<? extends Character> campA,
                            List<? extends Character> campB, ReentrantLock reentrantLock) {
        State state;
        // 眩晕状态
        state = character.checkDazeState();
        boolean error = false;
        while (state != null) {
            // 先释放锁
            if (reentrantLock != null && reentrantLock.isLocked()) {
                reentrantLock.unlock();
            }
            if (campA.contains(character)) {
                eventManager.notifyMessage(character.getName() + "眩晕中，无法进攻", 1);
            } else {
                eventManager.notifyMessage(character.getName() + "眩晕中，无法进攻", 2);
            }
            // 睡眠眩晕的时间
            DazeState dazeState = (DazeState) state;
            try {
                Thread.sleep(dazeState.getTimes());
            } catch (Exception e) {
                throw new InterruptException();
            }
            // 没死 移除眩晕状态
            if (!character.checkDead()) {
                if (campA.size() > 0 && campA.contains(character)) {
                    eventManager.notifyMessage(character.getName() + "眩晕中恢复", 1);
                } else if (campB.size() > 0) {
                    eventManager.notifyMessage(character.getName() + "眩晕中恢复", 2);
                } else {
                    eventManager.notifyMessage(character.getName() + "状态异常", 2);
                    error = true;
                }
                if (!error) {
                    state.reset();
                    character.getStates().remove(state);
                    // 再次上锁
                    if (reentrantLock != null) {
                        reentrantLock.lock();
                    }
                }
            }
            if (!error && !character.checkDead()) {
                // 再次检查是否还有眩晕状态
                state = character.checkDazeState();
            }
        }
    }

    private void launchPassiveCallSkill(List<? extends Character> campA, List<? extends Character> campB) {
        try {
            List<Skill> passiveSkills;
            for (Character character : campA) {
                if (character.getSkills() == null || character.getSkills().isEmpty()) {
                    continue;
                }
                passiveSkills = character.getSkills().stream().filter(e -> e.opportunityPassive() == 3).collect(Collectors.toList());
                if (!passiveSkills.isEmpty()) {
                    for (Skill passiveSkill : passiveSkills) {
                        Tuple<String, List<Character>> launch = passiveSkill.launch(character, campA, campB,
                                null);
                        eventManager.notifyMessage(launch.getKey(), 1);
                        setCampA(launch.getValue());
                    }
                }
            }
            for (Character character : campB) {
                passiveSkills = character.getSkills().stream().filter(e -> e.opportunityPassive() == 3).collect(Collectors.toList());
                if (!passiveSkills.isEmpty()) {
                    for (Skill passiveSkill : passiveSkills) {
                        Tuple<String, List<Character>> launch = passiveSkill.launch(character, campB, campA, null);
                        eventManager.notifyMessage(launch.getKey(), 2);
                        setCampB(launch.getValue());
                    }
                }
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    private void launchPassiveHaloSkill(List<? extends Character> campA, List<? extends Character> campB) {
        List<Skill> passiveSkills;
        for (Character character : campA) {
            if (character.getSkills() == null || character.getSkills().isEmpty()) {
                continue;
            }
            passiveSkills = character.getSkills().stream()
                    .filter(e -> e.opportunityPassive() != 0 && e.opportunityPassive() != 3).collect(Collectors.toList());
            if (!passiveSkills.isEmpty()) {
                for (Skill passiveSkill : passiveSkills) {
                    Tuple<String, List<Character>> launch = passiveSkill.launch(character, campA, campB, subFightExecutorService);
                    eventManager.notifyMessage(launch.getKey(), 1);
                    // 注册进来

                }
            }
        }
        for (Character character : campB) {
            passiveSkills = character.getSkills().stream().filter(e -> e.opportunityPassive() != 0).collect(Collectors.toList());
            if (!passiveSkills.isEmpty()) {
                for (Skill passiveSkill : passiveSkills) {
                    Tuple<String, List<Character>> launch = passiveSkill.launch(character, campB, campA, subFightExecutorService);
                    eventManager.notifyMessage(launch.getKey(), 2);
                }
            }
        }
    }

    // 发送一个事件
    public synchronized void publishDeadEvent(Character character, List<? extends Character> camp) {
        if (futureMap.size() > 0) {
            // 不为空，且还在
            if (character != null && camp.contains(character)) {
                eventManager.notifyMessage(character.getName() + "阵亡", 1);
                // 如果是人机对战 且死亡的是野怪
                if (battleType.equals(BattleTypeEnum.PVE) && camp.equals(campB)) {
                    character.release(campA, campB);
                    // 如果死亡的是野怪
                    if (character instanceof Monster) {
                        Monster monster = (Monster) character;
                        // 物品的掉落
                        fallingGoods.addAll(fallingGoodsCalculator.setMonster(monster).getFallingProduct(dropList));
                        // 金币的掉落
                        fallingCoin.add(fallCoinCalculator.getFallingCoin(monster));
                        // 经验值掉落
                        fallingExp += monster.getDeadExp();
                        // 检查任务情况
                        checkTaskForKillMonsters(character,getAttackHero());
                    }
                    camp.remove(character);

                }
                // 如果是人机对战 且死亡的是自己的人
                else if (battleType.equals(BattleTypeEnum.PVE) && camp.equals(campA)) {
                    // 先释放，有些技能是死亡时释放，也可以在这里实现
                    character.release(campB, campA);
                    camp.remove(character);
                }
            }
            if (camp.size() == 0) {
                if (camp.equals(campB)) {
                    // 经验
                    attackHero.addExp(fallingExp, max_monster_level);
                }
                // fallingGoods.forEach(e -> eventManager.notifyMessage(e.prettyPrint()));
                else {
                    eventManager.notifyMessage("失败，复活后重新进入副本", 1);
                }
                executorService.shutdown();
                return;
            }
            ScheduledFuture<?> future = futureMap.get(character);
            if (future != null) {
                future.cancel(true);
                futureMap.remove(character);
            }
        }
    }

    // 增加召唤
    public void publishCallEvent(Character character, List<? extends Character> camp, int seconds) {
        // 在camp阵营中，增加一个
        if (camp.equals(campA)) {
            // 从二级线程池中加载
            ScheduledFuture<?> futureA = subFightExecutorService.scheduleWithFixedDelay(
                    () -> {
                        // 回合制的必须要一个一个打
                        while (reentrantLockA.isLocked()) {
                            System.out.println("等待解锁");
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                // e.printStackTrace();
                            }
                        }
                        reentrantLockB.lock();
                        try {
                            if (campB != null && campB.size() > 0) {
                                if (character.getCurrentHp() > 0) {
                                    // 先检查眩晕情况
                                    checkState(character, campA, campB, reentrantLockB);
                                    if (character.getCurrentHp() <= 0) {
                                        return;
                                    }

                                    Tuple<String, List<Character>> attack = null;
                                    // 检查技能冷却情况
                                    List<Skill> skills = character.getSkills();
                                    if (!skills.isEmpty()) {
                                        // TODO 需要单独一个线程池来消减技能的冷却时间 目前先已攻击次数作为冷却时间，后面要改成以时间作为冷却时间
                                        skills.forEach(Skill::reduceInterval);
                                        //
                                        List<Skill> skillList = skills.stream().filter(e -> e.restInterval() == 0).collect(Collectors.toList());
                                        if (!skillList.isEmpty()) {
                                            attack = character.tryToLaunchSkill(skillList, campA,
                                                    campB, subFightExecutorService);
                                        }
                                    }
                                    // 技能没有发动 普通攻击
                                    if (attack == null) {
                                        attack = character.attack(campB);
                                    }
                                    eventManager.notifyMessage(attack.getKey(), 1);
                                    List<Character> defences = attack.getValue();
                                    for (Character defence : defences) {
                                        if (defence.checkDead()) {
                                            publishDeadEvent(defence, campB);
                                        }
                                    }
                                } else {
                                    publishDeadEvent(character, campA);
                                }
                            } else {
                                // 如果campB为空就结束了
                                publishDeadEvent(null, campB);
                            }
                        } catch (Exception exception) {
                            if (exception instanceof InterruptException) {

                            } else {
                                exception.printStackTrace();
                            }
                        } finally {
                            reentrantLockB.unlock();
                        }
                    }, 500 + (int) (((1 - (character.getSpeed()) * 1.0 / (2000 + character.getSpeed()))
                            * (1 - (character.getSpeed()) * 1.0 / (2000 + character.getSpeed()))) * 5000),
                    500 + (int) (((1 - (character.getSpeed()) * 1.0 / (2000 + character.getSpeed()))
                            * (1 - (character.getSpeed()) * 1.0 / (2000 + character.getSpeed()))) * 5000), TimeUnit.MILLISECONDS);
            // futureMap.put(character, futureA);
            timer.newTimeout(timeout -> {
                eventManager.notifyMessage(character.getName() + "时间到，消失", 1);
                futureA.cancel(true);
            }, seconds, TimeUnit.SECONDS);
        } else {


        }
    }

    public List<? extends Character> getCampA() {
        return campA;
    }

    public void setCampA(List<? extends Character> campA) throws IOException, ClassNotFoundException {
        this.campA = new ArrayList<>();
        // 这里主要是为了多线程的安全 其实最好还是使用 CopyOnWriteArrayList
        this.campA = Collections.synchronizedList(BeanCopyUtils.deepCopy(campA));
    }

    public List<? extends Character> getCampB() {
        return campB;
    }

    public void setCampB(List<? extends Character> campB) throws IOException, ClassNotFoundException {
        // 这里主要是为了多线程的安全 其实最好还是使用 CopyOnWriteArrayList
        this.campB = Collections.synchronizedList(BeanCopyUtils.deepCopy(campB));
    }

    public FallingGoods getFallingGoodsCalculator() {
        return fallingGoodsCalculator;
    }

    public void setFallingGoodsCalculator(FallingGoods fallingGoodsCalculator) {
        this.fallingGoodsCalculator = fallingGoodsCalculator;
    }

    public FallingCoin getFallCoinCalculator() {
        return fallCoinCalculator;
    }

    public void setFallCoinCalculator(FallingCoin fallCoinCalculator) {
        this.fallCoinCalculator = fallCoinCalculator;
    }

    public HeroWrapper getAttackHero() {
        return attackHero;
    }

    public void setAttackHero(HeroWrapper attackHero) {
        this.attackHero = attackHero;
    }

    public int getMax_monster_level() {
        return max_monster_level;
    }

    public void setMax_monster_level(int max_monster_level) {
        this.max_monster_level = max_monster_level;
    }

    public List<MapTemplate.Drop> getDropList() {
        return dropList;
    }

    public void setDropList(List<MapTemplate.Drop> dropList) {
        this.dropList = dropList;
    }


}
